/// 底层Poll阻塞等待IO事件到来，如果没有任何IO事件，需要有立即退出的机制
/// 一个提供IO事件的通知机制, Poll可以立即从阻塞的wait中返回
/// 考虑到对性能资源要求很低, 选择inet socket，各个平台都通用
use crate::Error;
use core::mem::{self, MaybeUninit};
use core::ptr;
use crate::platform::inet_pton;

pub struct Waker {
    pub fd: i32,
}

impl Drop for Waker {
    fn drop(&mut self) {
        unsafe { libc::close(self.fd) };
    }
}

impl Waker {
    pub fn new() -> Result<Self, Error> {
        let fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
        if fd == -1 {
            return Err(Error::last_error());
        }
        let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) } | libc::O_NONBLOCK;
        unsafe { libc::fcntl(fd, libc::F_SETFL, flags) };

        let mut addr = MaybeUninit::<libc::sockaddr_in>::zeroed();
        let addrlen = mem::size_of::<libc::sockaddr_in>() as libc::socklen_t;

        let addr = unsafe { addr.assume_init_mut() };
        addr.sin_family = libc::AF_INET as libc::sa_family_t;
        let paddr = ptr::addr_of_mut!(addr.sin_addr.s_addr) as *mut u8;
        let local = b"127.0.0.1\0" as *const u8;
        unsafe { inet_pton(libc::AF_INET, local, paddr) };

        let ret = unsafe { libc::bind(fd, addr as *const _ as *const libc::sockaddr, addrlen) };
        if ret != 0 {
            unsafe { libc::close(fd) };
            return Err(Error::last_error());
        }
        Ok(Self { fd })
    }

    pub fn wake(&self) {
        let mut addr = MaybeUninit::<libc::sockaddr_in>::zeroed();
        let mut addrlen = mem::size_of_val(&addr) as libc::socklen_t;
        unsafe {
            libc::getsockname(
                self.fd,
                addr.as_mut_ptr() as *mut libc::sockaddr,
                ptr::addr_of_mut!(addrlen),
            )
        };
        let msg = &self.fd as *const _ as *const libc::c_void;
        let msglen = mem::size_of_val(&self.fd);
        unsafe {
            libc::sendto(
                self.fd,
                msg,
                msglen,
                0,
                addr.as_ptr() as *const libc::sockaddr,
                addrlen,
            )
        };
    }

    pub fn awaken(&self) -> bool {
        let mut fd = [-1_i32; 2];
        let size = unsafe {
            libc::recv(
                self.fd,
                &mut fd as *mut _ as *mut libc::c_void,
                mem::size_of_val(&fd),
                0,
            )
        };
        size as usize == mem::size_of::<i32>() && fd[0] == self.fd
    }
}
